package com.googlecode.layout4j.parser;

import java.io.FileReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.tidy.Tidy;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Converts admin (jquery-friendly) HTML to LayoutML.
 * 
 * XXX: Rename HtmlToXmlConverter
 * 
 * @author Matt Humphreys
 */
/*
 * Uses JTidy and SAX. Alternatives below. They all have Maven builds available.
 * XXX: Better to use more forgiving parser (Jericho or NekoHTML).
 * <ul>
 * <li>Jericho</li>
 * <li>TagSoup: SAX-compliant. No examples.</li>
 * <li>NekoHTML: I can't get the SAX support working. DOM support should work though.</li>
 * <li>HTML Parser: Examples aren't very clear.</li>
 * </ul>
 * 
 * XXX: Use ContentExtractor
 */
public class HtmlToXmlConverter {
	private static Logger log = LoggerFactory.getLogger(HtmlToXmlConverter.class);
	private static final String XML_FURNITURE_START = 
		"<?xml version='1.0' encoding='UTF-8'?>"
		+ "<!DOCTYPE html>"
		+ "<root xmlns:cms='http://code.google.com/p/layout4j/'>";
	// xmlns:style='http://www.zingcms.org/xmlns/style'
	private static final String XML_FURNITURE_END = "</root>";
	private static final String XSLT_FILENAME = "htmlToXml.xsl";

	public static String toXml(String original, boolean addXmlFurniture) throws LayoutException {
		String input = original;	// ???
		Reader reader = new StringReader(input);
		return toXml(reader, addXmlFurniture);
	}
	
	public static String toXml(Reader sourceReader, boolean addXmlFurniture) throws LayoutException {
		Writer tidySourceWriter = new StringWriter();
		HtmlToXmlConverter.tidy(sourceReader, tidySourceWriter);
		if (log.isDebugEnabled()) {
			log.debug("Tidied input: " + tidySourceWriter.toString());
		}
		//System.out.println("Tidied input: " + tidySourceWriter.toString());

	    TransformerFactory tFactory = TransformerFactory.newInstance();
	    Transformer transformer = null;
		try {
			transformer = tFactory.newTransformer(new StreamSource(
					HtmlToXmlConverter.class.getResourceAsStream(XSLT_FILENAME)));
		} catch (TransformerConfigurationException e) {
			throw new LayoutException(e);
		}
		
	    StreamSource tidySource = new StreamSource(initReader(tidySourceWriter, true));
	    //tidySource.setSystemId(systemId);
	    StringWriter resultWriter = new StringWriter();
	    try {
			transformer.transform(tidySource, new StreamResult(resultWriter));
		} catch (TransformerException e) {
			throw new LayoutException(e);
		}
		
		// XXX: Should this be streamed? e.g. http://ostermiller.org/convert_java_writer_reader.html
	    return resultWriter.toString();
	}
	
	private static Reader initReader(Writer writer, boolean addXmlFurniture) {
		if (addXmlFurniture) {
			return new StringReader(XML_FURNITURE_START + writer.toString() + XML_FURNITURE_END);
		} else {
			return new StringReader(writer.toString());
		}
	}
	
	private static void tidy(Reader reader, Writer writer) {
		Tidy tidy = new Tidy();		// obtain a new Tidy instance
		tidy.setXHTML(true);
		tidy.setPrintBodyOnly(true);
		//tidy.setXmlPi(true);		// true if tidy should add <?xml?> for XML docs
		tidy.setForceOutput(false);
		tidy.parse(reader, writer); // run tidy, providing an input and output stream
	}

}
